﻿namespace Contoso.ActiveDirectory {

    #region Usings
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.DirectoryServices;
    using System.Diagnostics;
    using System.Threading;
    #endregion

    /// <summary>
    /// When querying for a SiteServer, the base DN must be the Configuration Naming Context
    /// </summary>
    [Serializable]
    public class SiteServer : ADBaseObjectProperties {

        #region Members
        #region Fields
        public static readonly new string[] AttributesToGet;

        public static readonly string ObjectCategoryShortName = "server"; 
        #endregion

        /// <summary>
        /// The list of transports (IP, SMTP) for which this server is preferred.
        /// </summary>
        public IReadOnlyList<string> BridgeHeadTransportList { get; private set; }

        public string DnsHostName { get; private set; }

        public InstanceTypeFlags InstanceType { get { return instanceType; } }
        private InstanceTypeFlags instanceType;

        /// <summary>
        /// http://msdn.microsoft.com/en-us/library/cc220288%28v=prot.10%29.aspx
        /// For an Active Directory instance, this attribute identifies the state of the global catalog (GC) on the directory system agent (DSA).
        /// </summary>
        /// <remarks>Constructed</remarks>
        public bool MSDSIsGC { get; private set; }

        /// <summary>
        /// http://msdn.microsoft.com/en-us/library/cc220290%28v=prot.10%29.aspx
        /// For an Active Directory instance, this attribute identifies whether the DSA is a read-only DSA.
        /// </summary>
        /// <remarks>Constructed</remarks>
        public bool MSDSIsRODC { get; private set; }

        /// <summary>
        /// This object may not exist.  Ensure that it is checked for null.
        /// </summary>
        public NTDSSettings NTDSSettings {
            get {
                if (ntdsSettings == null) {
                    this.GetNTDSSettings();
                }

                return ntdsSettings;
            }
        }
        private NTDSSettings ntdsSettings;

        /// <summary>
        /// The DN of the Domain Controller in the naming context
        /// </summary>
        public string ServerReference { get; private set; }
        #endregion

        #region Constructors
        static SiteServer() {
            var attributesToGet = new List<string>();
            attributesToGet.AddRange(new string[]
            {
                "bridgeHeadTransportList",
                "dnsHostName",
                "msDS-IsGC",
                "msDS-IsRODC",
                "serverReference"
            });

            attributesToGet.AddRange(ADBaseObjectProperties.AttributesToGet);

            AttributesToGet = attributesToGet.Distinct().ToArray();
            attributesToGet = null;
        }
        public SiteServer() {
            this.Initialize();
        }

        public SiteServer(DirectoryEntry directoryEntry, bool destroyInputObject = true)
            : base(directoryEntry, destroyInputObject) {
            if (this.GetType().Name == typeof(SiteServer).Name) {
                this.Attributes = null;
            }
        }

        public SiteServer(SearchResult result, bool destroyInputObject = true)
            : base(result, destroyInputObject) {
            if (this.GetType().Name == typeof(SiteServer).Name) {
                this.Attributes = null;
            }
        }

        public SiteServer(List<string> ldifOutput)
            : base(ldifOutput) {
            if (this.GetType().Name == typeof(SiteServer).Name) {
                this.Attributes = null;
            }
        }
        #endregion

        #region Methods

        public void GetNTDSSettings() {

            if (!this.MSDSIsRODC) {
                var filter = string.Format("(objectCategory={0})", NTDSSettings.ObjectCategoryShortName);
                ntdsSettings = ADDataAccess.GetADObjectProperties<NTDSSettings>(
                    this.DN, NTDSSettings.AttributesToGet, filter, PartitionScope.LocalPartition)
                    .FirstOrDefault();
            }
            else {
                var filter = string.Format("(objectCategory={0})", NTDSSettingsRO.ObjectCategoryShortName);
                ntdsSettings = ADDataAccess.GetADObjectProperties<NTDSSettingsRO>(
                    this.DN, NTDSSettingsRO.AttributesToGet, filter, PartitionScope.LocalPartition)
                    .FirstOrDefault();
            }
        }

        protected override void Initialize() {
            base.Initialize();

            this.DnsHostName = string.Empty;
            this.ServerReference = string.Empty;
            this.BridgeHeadTransportList = new List<string>();
        }

        protected override void ProcessResults() {

            try {
                base.ProcessResults();

                if (this.Attributes.ContainsKey("bridgeHeadTransportList")) {
                    this.BridgeHeadTransportList = ADDataAccess.GetMultiValuedAttribute<string>("bridgeHeadTransportList", this.Attributes);
                }

                if (this.Attributes.ContainsKey("dNSHostName")) {
                    this.DnsHostName = this.Attributes["dNSHostName"][0].ToString();
                }
                if (this.Attributes.ContainsKey("instanceType")) {
                    Enum.TryParse<InstanceTypeFlags>(this.Attributes["instanceType"][0].ToString(), result: out instanceType);
                }
                if (this.Attributes.ContainsKey("serverReference")) {
                    this.ServerReference = this.Attributes["serverReference"][0].ToString();
                }

                if (this.Attributes.ContainsKey("msDS-IsGC")) {
                    this.MSDSIsGC = Convert.ToBoolean(this.Attributes["msDS-IsGC"][0]);
                }
                if (this.Attributes.ContainsKey("msDS-IsRODC")) {
                    this.MSDSIsRODC = Convert.ToBoolean(this.Attributes["msDS-IsRODC"][0]);
                }

                this.IsValid = !string.IsNullOrWhiteSpace(this.DN)
                    && this.ObjectClass.Any(x => string.Equals(x, ObjectCategoryShortName, StringComparison.OrdinalIgnoreCase));
            }
            catch (Exception e) {
                Debug.WriteLine(string.Format("{0}  [ThreadID: {1}]  {2} Error: {3}", DateTime.Now.TimeOfDay, Thread.CurrentThread.ManagedThreadId, CustomExtensions.CurrentMethodName(), e.VerboseExceptionString()));
                throw;
            }
        }

        public string ToFormattedString() {
            var info = new StringBuilder();

            info.AppendLine(string.Format(" Server DN: {0}", this.DN ?? "NULL"));
            info.AppendLine(string.Format(" Server DNS HostName: {0}", this.DnsHostName ?? "NULL"));
            info.AppendLine(string.Format(" Server Instance Type: {0}", this.InstanceType));
            info.AppendLine(string.Format(" Server Naming Context DN: {0}", this.ServerReference ?? "NULL"));

            return info.ToString();
        }
        #endregion
    }
}
